package frege.control.trans.MaybeT where

import frege.control.trans.MonadTrans
import frege.control.trans.MonadIO

{-- 
The parameterizable maybe monad, obtained by composing an arbitrary
monad with the 'Maybe' monad.

Computations are actions that may produce a value or fail.

The 'return' function yields a successful computation, while (>>=)
sequences two subcomputations, failing on the first error.
-}
data MaybeT m a = MaybeT { run :: m (Maybe a) }

-- | Transform the computation inside a @MaybeT@.
mapMaybeT :: (m (Maybe a) -> n (Maybe b)) -> MaybeT m a -> MaybeT n b
mapMaybeT f mt = MaybeT $ f $ MaybeT.run mt

instance Functor Functor m => MaybeT m where
    fmap f = mapMaybeT (fmap (fmap f))

--instance (Functor m, Monad m) => Alternative (MaybeT m) where
--    empty = mzero
--    (<|>) = mplus

instance Monad Monad m => MaybeT m where
    --fail _ = MaybeT (return Nothing)
    return mt = lift $ return mt
    x >>= f = MaybeT $ do
        v <- MaybeT.run x
        case v of
            Nothing -> return Nothing
            Just y  -> MaybeT.run (f y)
            
instance MonadFail Monad m => MaybeT m where
    fail _ = MaybeT (return Nothing)            

instance MonadPlus Monad m => MaybeT m where
    mzero = MaybeT (return Nothing)
    mplus x y = MaybeT $ do
        v <- MaybeT.run x
        case v of
            Nothing -> MaybeT.run y
            Just _  -> return v

instance MonadTrans MaybeT where
    lift mt = MaybeT $ liftM Just mt

instance MonadIO MonadIO m => MaybeT m where
    liftIO io = lift $ liftIO io